#include "population.h"
#include "terminals/pod.h"
#include "functions/arithmetic.h"
#include "subfactory.h"
#include "function.h"
#include "individual.h"
#include "create.h"
#include "terminals/randompod.h"

#include <iostream>
#include <complex>
#include <random>
#include <functional>
#include <typeinfo>


using namespace rmitgprime;

typedef Function<double, 2u> FunctionType;
typedef boost::function<FunctionType * ()> FunctionCtor;
typedef Terminal<double> TerminalType;
typedef boost::function<TerminalType * (double)> TerminalCtor;
typedef std::complex<double> ComplexDouble;
typedef Terminal<ComplexDouble> ComplexTerminal;
typedef boost::function<ComplexTerminal * (ComplexDouble)> ComplexCtor;
typedef boost::function<TerminalType *()> RandTerminalCtor;

/*
template <>
std::vector<FunctionCtor> Factory<FunctionType>::factory = std::vector<FunctionCtor>();
template <>
std::vector<TerminalCtor> Factory<TerminalType, double>::factory = std::vector<TerminalCtor>();
template <>
std::vector<ComplexCtor> Factory<ComplexTerminal, std::complex<double>>::factory = std::vector<ComplexCtor>();
template <>
std::vector<RandTerminalCtor> Factory<TerminalType>::factory = std::vector<RandTerminalCtor>();
*/

template <>
boost::function<double ()> RandomPOD<double>::generator = std::bind(std::uniform_real_distribution<double>(0, 1), std::mt19937_64());


int main(int argc, char **argv)
{
    // Random numbers.
    std::mt19937_64 engine;

    RandomPOD<double> a, b;
    POD<ComplexDouble> c(ComplexDouble(5, 3));

    std::cout << *a << "+" << *b << "=" << a + b << std::endl;
    std::cout << *a << "*" << *b << "=" << a * b << std::endl;
    std::cout << *a << "-" << *b << "=" << a - b << std::endl;
    std::cout << *a << "/" << *b << "=" << a / b << std::endl;

    std::cout << *c << "+" << *c << "=" << c + c << std::endl;
    std::cout << *c << "*" << *c << "=" << c * c << std::endl;
    std::cout << *c << "-" << *c << "=" << c - c << std::endl;
    std::cout << *c << "/" << *c << "=" << c / c << std::endl;


    // Register functions
	Factory<FunctionType> function_factory;
    function_factory.registersubclass<Add<double, 2u>>();
    function_factory.registersubclass<Subtract<double, 2u>>();
    function_factory.registersubclass<Divide<double, 2u>>();
    function_factory.registersubclass<Multiply<double, 2u>>();
    // Register terminals
	Factory<TerminalType, double> terminal_double_factory;
    terminal_double_factory.registersubclass<POD<double>>();
	
    // Factory<ComplexTerminal, std::complex<double>>::registersubclass<POD<std::complex<double>>>();
    Factory<TerminalType> terminal_factory;
	terminal_factory.registersubclass<RandomPOD<double>>();

    auto	function_dist = std::uniform_int_distribution<size_t>(0, function_factory.size() - 1),
			terminal_dist = std::uniform_int_distribution<size_t>(0, terminal_factory.size() - 1);

    // auto function_index = std::bind(function_dist, engine), terminal_index = std::bind(terminal_dist, engine);
    // auto coinflip = std::bind(std::uniform_int_distribution<unsigned>(0, 1), engine);

    unsigned count = 0;
    while(count++ < 10)
    {
#if (0)
        if(coinflip())
        {
            std::cout << typeid(*function_factory.spawn(function_index())).name() << std::endl;
        }
        else
        {
            // std::cout << **terminal_factory.spawn(terminal_index()) << std::endl;
            std::cout << typeid(*terminal_factory.spawn(terminal_index())).name() << std::endl;
        }
#else
        std::cout << engine() << std::endl;
        auto node = full_new(0, function_dist, terminal_dist, engine, terminal_double_factory, function_factory);
        std::cout << "node: " << typeid(*node).name() << ": " << **node << std::endl;
#endif
        // sleep(1);
    }

    // Population<double> pop;
    // Individual<double> I;


    return 0;
}
